/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = rtp_core.c                                         */
/*  NOTE      =                                                    */
/*  DATE      = 2015/08/20 by songbb                               */
/*******************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <frct_detect_hash.h>

#include "frlog.h"

#include "frct_comm.h"
#include "frexp_comm.h"

#include "rtp_comm.h"
#include "rtp_header.h"
#include "config.h"

extern int g_i_connect_in_collect;

/*******************************************************************************
*  FUNC     :  RTP Session初始化
*  ARGS     :  *p_st_session                            (L7层session信息)
*  RTN      :  0                                        (FR_SUCC)
*              1                                        (FR_FAIL)
*  NOTE     :
*******************************************************************************/
FR_STATIC int __rtp_private_session_init(L7_session *p_st_session)
{
    RTP_PRINT_INFO("session init\n");
    L7session_rtp_private *p_st_rtp_private = NULL;      // RTP的私有信息
    L7session_rtp *p_st_rtp_sess = NULL;                 // RTP的私有session信息

    // 1. 初始化RTP的私有session信息
    if ((p_st_rtp_sess = (L7session_rtp *) frmalloc(sizeof(L7session_rtp))) == NULL)
    {
        RTP_MALLOCK_ERR;
        goto error;
    }
    memset(p_st_rtp_sess, 0x00, sizeof(L7session_rtp));
    p_st_rtp_sess->i_a2b_is_ff = APM_RTP_FLAG_UNSET;
    p_st_rtp_sess->l_a2b_seq = APM_RTP_FLAG_UNSET;
    p_st_rtp_sess->i_b2a_is_ff = APM_RTP_FLAG_UNSET;
    p_st_rtp_sess->l_b2a_seq = APM_RTP_FLAG_UNSET;
    p_st_rtp_sess->c_skip_this_ct = FR_NO;
    p_st_rtp_sess->stream_type = APM_RTP_STREAM_TYPE_UNKNOWN;

    // 2. 创建和初始化RTP的私有信息
    if ((p_st_rtp_private = (L7session_rtp_private *) frmalloc(sizeof(L7session_rtp_private))) == NULL)
    {
        RTP_MALLOCK_ERR;
        goto error;
    }
    memset(p_st_rtp_private, 0x00, sizeof(L7session_rtp_private));
    p_st_rtp_private->p_st_sess = p_st_rtp_sess;
    p_st_rtp_private->p_st_session = p_st_session;

    // 3. 设置L7_session的p_session_private信息
    frct_set_session_private(p_st_session, (void *)p_st_rtp_private);

    return FR_SUCC;
error:
    rtp_free((void **) &p_st_rtp_private);
    return FR_FAIL;
}

/*******************************************************************************
*  FUNC     :  获取关联的SIP连接的信息然后上报
*  ARGS     :  *p_st_conn                           (ct信息)
*           :  *p_st_packet                         (协议分析入力数据)
*           :  *p_st_hash_report                    (保存Frreport_data)
*           :  *p_i_analyze_ret                     (返回码)
*  RTN      :
*  NOTE     :
*******************************************************************************/
FR_STATIC void __rtp_get_related_sip_info_and_report(Conntrack *p_st_conn
                                                    , L7session_rtp *p_st_sess
                                                    , Frhash_header *p_st_hash_report
                                                    , int *p_i_analyze_ret)
{
    unsigned int ui_src_ip;
    unsigned short us_src_port;
    unsigned int ui_dst_ip;
    unsigned short us_dst_port;
    if (frexpect_get_exp_ip(p_st_conn, &ui_src_ip, &us_src_port, &ui_dst_ip, &us_dst_port) != FR_FAIL)
    {
//            RTP_PRINT_ALERT("exp_src_port:[%u] exp_dst_port:[%u] a_port:[%u] b_port:[%u] tv_sec:[%lu] tv_usec:[%lu]\n", us_src_port, us_dst_port, p_st_sess->us_a_port, p_st_sess->us_b_port, p_st_sess->st_a2b_fft.tv_sec, p_st_sess->st_a2b_fft.tv_usec);
//            RTP_PRINT_ALERT("exp_src_ip:[%u] exp_dst_ip:[%u] a_ip:[%u] b_ip:[%u]\n", ui_src_ip, ui_dst_ip, p_st_sess->ui_a_ip, p_st_sess->ui_b_ip);
        if (ui_src_ip == p_st_sess->ui_a_ip &&
            us_src_port == p_st_sess->us_a_port &&
            ui_dst_ip == p_st_sess->ui_b_ip &&
            us_dst_port == p_st_sess->us_b_port &&
            p_st_sess->ul_a2b_ffft != 0L)
        {
            rtp_report(p_st_conn, p_st_hash_report, p_st_sess->ul_a2b_fbt, p_st_sess->ul_a2b_bytes,
                       p_st_sess->ul_a2b_ffft, p_st_sess->ul_a2b_pluff, p_i_analyze_ret);

            // 这个ct不再做分析
            p_st_sess->c_skip_this_ct = FR_YES;
            frct_drop_pcap(p_st_conn, 1);
        }
        else if (ui_src_ip == p_st_sess->ui_b_ip &&
                 us_src_port == p_st_sess->us_b_port &&
                 ui_dst_ip == p_st_sess->ui_a_ip &&
                 us_dst_port == p_st_sess->us_a_port &&
                 p_st_sess->ul_b2a_ffft != 0L)
        {
            rtp_report(p_st_conn, p_st_hash_report, p_st_sess->ul_b2a_fbt, p_st_sess->ul_b2a_bytes,
                       p_st_sess->ul_b2a_ffft, p_st_sess->ul_b2a_pluff, p_i_analyze_ret);

            // 这个ct不再做分析
            p_st_sess->c_skip_this_ct = FR_YES;
            frct_drop_pcap(p_st_conn, 1);
        }
    }
    else
        RTP_PRINT_EMERG("can not find the related sip ct information\n");

    return;
}

/*******************************************************************************
*  FUNC     :  上报RTP响应信息，仅包含一部分内容，其他内容由collect补充
*  ARGS     :  *p_st_conn                           (ct信息)
*           :  *p_st_packet                         (协议分析入力数据)
*           :  *p_st_hash_report                    (保存Frreport_data)
*           :  *p_i_analyze_ret                     (返回码)
*  RTN      :
*  NOTE     :
*******************************************************************************/
FR_STATIC void __report_rtp_response(Conntrack *p_st_conn
                                    , L7session_rtp *p_st_sess
                                    , Frhash_header *p_st_hash_report
                                    , int *p_i_analyze_ret)
{
    if (p_st_sess->ul_a2b_ffft != 0L)
    {
        rtp_report_resp_only(p_st_conn, p_st_hash_report, p_st_sess->ul_a2b_fbt, p_st_sess->ul_a2b_bytes,
                   p_st_sess->ul_a2b_ffft, p_st_sess->ul_a2b_pluff, p_i_analyze_ret);

        // 这个ct不再做分析
        p_st_sess->c_skip_this_ct = FR_YES;
        frct_drop_pcap(p_st_conn, 1);
    }
    else if (p_st_sess->ul_b2a_ffft != 0L)
    {
        rtp_report_resp_only(p_st_conn, p_st_hash_report, p_st_sess->ul_b2a_fbt, p_st_sess->ul_b2a_bytes,
                   p_st_sess->ul_b2a_ffft, p_st_sess->ul_b2a_pluff, p_i_analyze_ret);

        // 这个ct不再做分析
        p_st_sess->c_skip_this_ct = FR_YES;
        frct_drop_pcap(p_st_conn, 1);
    }

    return;
}

/*******************************************************************************
*  FUNC     :  判断当前数据链路是否是RTP协议的
*  ARGS     :  *p_st_conn                           (ct信息)
*           :  *p_st_packet                        (协议分析入力数据)
*  RTN      :  参照module.h中APM_L7_IS_的定义
*  NOTE     :
*******************************************************************************/
int rtp_detect(Conntrack *p_st_conn
            , Apm_packet *p_st_packet)
{
    RTP_PRINT_INFO("rtp_detect\n");
    Rtp_detect_session *p_st_detect_sess = NULL;

    if (p_st_conn == NULL || p_st_packet == NULL || p_st_packet->p_payload == NULL || p_st_packet->ui_payload_len == 0)
        return APM_L7_IS_NO;

#if APM_RTP_DBG
    char arr_ip_src[56], arr_ip_dst[56];
    snprintf(arr_ip_src, sizeof(arr_ip_src), "%s", int_ntoa(p_st_packet->src_ip));
    snprintf(arr_ip_dst, sizeof(arr_ip_src), "%s", int_ntoa(p_st_packet->dst_ip));
    RTP_PRINT_TIMEVAL("PacketIn", p_st_packet->st_timestamp);
    RTP_PRINT_DBG("Src:[%s:%d] Dest:[%s:%d] Length:[%u]\n", arr_ip_src, p_st_packet->src_port, arr_ip_dst, p_st_packet->dst_port, p_st_packet->ui_payload_len);
#endif

    int i_ret = APM_L7_IS_UNKNOWN;

    if ((p_st_detect_sess = (Rtp_detect_session *) frct_detect_data_get(p_st_conn, MODULE_L7PROTO)) == NULL)
    {
        if ((p_st_detect_sess = (Rtp_detect_session *) frmalloc(sizeof(Rtp_detect_session))) == NULL)
        {
            RTP_MALLOCK_ERR;
            goto quit;
        }
        rtp_init_detect_sess(&p_st_detect_sess);

        if (frct_detect_hash_add(p_st_conn, MODULE_L7PROTO, (void *) p_st_detect_sess) != FR_SUCC)
        {
            rtp_free((void **) &p_st_detect_sess);
            goto quit;
        }
    }

    i_ret = rtp_header_detect(p_st_detect_sess, p_st_packet->p_payload, p_st_packet->ui_payload_len, p_st_packet->dst_ip, p_st_packet->dst_port);
    if (i_ret == APM_L7_IS_YES)
    {
        // 设置方向
        if (p_st_packet->i_direction == FR_DIRECT_UNKNOWN || frct_get_direct_known(p_st_conn) == FR_DIRECT_UNKNOWN)
        {
            // XXX: 这里始终假定传输的方向为服务器向客户端
            if (frcap_set_cs_srv(p_st_conn, p_st_packet->src_ip, p_st_packet->src_port) != FR_SUCC)
                return APM_L7_IS_UNKNOWN;
        }

        if (g_i_connect_in_collect == FR_NO)
            frct_set_statIp(p_st_conn, MODULE_L7PROTO, MODULE_L7PROTO);
    }

quit:
    if (i_ret == APM_L7_IS_UNKNOWN)
        RTP_PRINT_ERR("detect unknown\n\n");
    else
        RTP_PRINT_ERR("detect yes\n\n");

    return i_ret;
}

/*******************************************************************************
*  FUNC     :  RTP协议解析
*  ARGS     :  *p_st_conn                           (ct信息)
*           :  *p_st_packet                        (协议分析入力数据)
*           :  *p_st_hash_report                    (保存Frreport_data)
*  RTN      :  参照module.h中APM_L7_ANALYZE_的定义
*  NOTE     :
*******************************************************************************/
int rtp_analyze(Conntrack *p_st_conn
                , Apm_packet *p_st_packet
                , Frhash_header *p_st_hash_report)
{
    RTP_PRINT_INFO("rtp_analyze\n");
    L7_session *p_st_session = NULL;
    L7session_rtp_private *p_st_session_private = NULL;
    L7session_rtp *p_st_sess = NULL;
    char c_marker;
    char arr_ip_src[56], arr_ip_dst[56];
    snprintf(arr_ip_src, sizeof(arr_ip_src), "%s", int_ntoa(p_st_packet->src_ip));
    snprintf(arr_ip_dst, sizeof(arr_ip_dst), "%s", int_ntoa(p_st_packet->dst_ip));
    unsigned short us_seq;
    unsigned int ui_timestamp;
    char *p_payload = NULL;
    unsigned int ui_payload_len;
    Rtp_detect_session *p_st_detect_sess = NULL;
    unsigned int ui_frame_len = 0;
    unsigned int ui_skip_len = 0;
    int i_analyze_ret = APM_L7_ANALYZE_SUCC;

    if (p_st_conn == NULL || p_st_packet == NULL
        || p_st_hash_report == NULL)
        return APM_L7_ANALYZE_FAIL;

    p_st_session = frct_get_session(p_st_conn);

    RTP_PRINT_TIMEVAL("PacketIn", p_st_packet->st_timestamp);
    RTP_PRINT_DBG("Src:[%s:%d] Dest:[%s:%d] SessionID:[%s] PayloadLen:[%d]\n",
                  arr_ip_src,
                  p_st_packet->src_port,
                  arr_ip_dst,
                  p_st_packet->dst_port,
                  p_st_session->arr_session_id,
                  p_st_packet->ui_payload_len);

    /*
    * 如果入力p_st_packet是FIN包, 说明本次connection结束,
    * 释放本次session信息. 如果入力p_st_packet是SYN包,说明新的
    * connection开始了, 释放上次可能残留的session信息
    */
    if (p_st_packet->em_analyz_cmd == FRANALYZ_CMD_FIN || p_st_packet->em_analyz_cmd == FRANALYZ_CMD_SYN)
    {
        if (p_st_packet->em_analyz_cmd == FRANALYZ_CMD_SYN)
            RTP_PRINT_INFO("SYN PACKET\n");
        else if (p_st_packet->em_analyz_cmd == FRANALYZ_CMD_FIN)
            RTP_PRINT_INFO("FIN PACKET\n");
        if (p_st_session->p_session_private != NULL)
        {
            rtp_session_destory(p_st_session->p_session_private);
            p_st_session->p_session_private = NULL;
        }
        RTP_PRINT("### i_analyze_ret:[%d]\n\n", APM_L7_ANALYZE_SUCC);
        return APM_L7_ANALYZE_SUCC;
    }

    // 本次链路的第一个有效packet包,创建链路
    if (p_st_session->p_session_private == NULL)
    {
        // 初始化RTP的session数据
        if (__rtp_private_session_init(p_st_session) != FR_SUCC)
            goto rtp_analyze_fail;

        // 设置session uuid
        fruuid(p_st_session->arr_session_id);

        if (g_i_connect_in_collect == FR_NO)
            frct_set_statIp(p_st_conn, MODULE_L7PROTO, MODULE_L7PROTO);
    }

    // 设置方向
    if (p_st_packet->i_direction == FR_DIRECT_UNKNOWN || frct_get_direct_known(p_st_conn) == FR_DIRECT_UNKNOWN)
    {
        // XXX: 这里始终假定传输的方向为服务器向客户端
        if (frcap_set_cs_srv(p_st_conn, p_st_packet->src_ip, p_st_packet->src_port) != FR_SUCC)
                return APM_L7_IS_UNKNOWN;
    }

    // 打印方向
    switch (p_st_packet->i_direction)
    {
        case FR_DIRECT_CLIENT2SRV:
            RTP_PRINT("client["KLIMEGREEN"%s:%d"RESET"] to server["KLIMEGREEN"%s:%d"RESET"] ["KYELLOW"%d"RESET"] ->>>>>>>>>>>>>>>>>>>>>>>\n",
                      arr_ip_src,
                      p_st_packet->src_port,
                      arr_ip_dst,
                      p_st_packet->dst_port,
                      p_st_packet->ui_payload_len);
            break;
        case FR_DIRECT_SRV2CLIENT:
            RTP_PRINT("server["KLIMEGREEN"%s:%d"RESET"] to client["KLIMEGREEN"%s:%d"RESET"] ["KYELLOW"%d"RESET"] <<<<<<<<<<<<<<<<<<<<<<<-\n",
                      arr_ip_src,
                      p_st_packet->src_port,
                      arr_ip_dst,
                      p_st_packet->dst_port,
                      p_st_packet->ui_payload_len);
            break;
        default:
            RTP_PRINT("unknown direction, src["KLIMEGREEN"%s:%d"RESET"], dest["KLIMEGREEN"%s:%d"RESET"]\n",
                      arr_ip_src,
                      p_st_packet->src_port,
                      arr_ip_dst,
                      p_st_packet->dst_port);
            goto rtp_analyze_fail;
    }

    p_st_session_private = (L7session_rtp_private *) frct_get_session_private(p_st_session);
    p_st_sess = p_st_session_private->p_st_sess;

    if (p_st_sess->c_skip_this_ct == FR_YES)
    {
        RTP_PRINT_INFO("skip this pcap...\n\n");
        return APM_L7_ANALYZE_SUCC;
    }

    // 如果第一个完整帧结束,即被设置了完整帧结束时间。如果rtp比sip先解析完成,会进入这个逻辑
    if (p_st_sess->ul_a2b_ffft != 0L || p_st_sess->ul_b2a_ffft != 0L)
    {
        RTP_PRINT_EMERG("first full frame is done, start to find the related sip ct information again...\n");
        __rtp_get_related_sip_info_and_report(p_st_conn, p_st_sess, p_st_hash_report, &i_analyze_ret);

        return APM_L7_ANALYZE_SUCC;
    }

    if ((p_st_detect_sess = (Rtp_detect_session *) frct_detect_data_get(p_st_conn, MODULE_L7PROTO)) != NULL)
    {
        RTP_PRINT_WARN("stream type:[%s]\n", APM_RTP_STREAMTYPE2STR(p_st_detect_sess->stream_type));
        p_st_sess->stream_type = p_st_detect_sess->stream_type;
        frct_detect_hash_del(p_st_conn, MODULE_L7PROTO);
    }

    if (p_st_sess->ui_a_ip == 0 ||
            p_st_sess->ui_b_ip == 0 ||
            p_st_sess->us_a_port == 0 ||
            p_st_sess->us_b_port == 0)
    {
        p_st_sess->ui_a_ip = p_st_packet->src_ip;
        p_st_sess->ui_b_ip = p_st_packet->dst_ip;
        p_st_sess->us_a_port = p_st_packet->src_port;
        p_st_sess->us_b_port = p_st_packet->dst_port;
    }

    p_payload = p_st_packet->p_payload;
    ui_payload_len = p_st_packet->ui_payload_len;

do_frame:
    switch (rtp_header_unpack(p_st_sess, p_payload, ui_payload_len, &c_marker, &us_seq, &ui_timestamp, &ui_frame_len, &ui_skip_len))
    {
        case 1:
            goto rtp_analyze_todo;
        case 2:
            goto rtp_analyze_fail;
        default:
            ;
    }

    /**
     * XXX: 特别说明,一种丢包情况下,会导致关键帧的统计异常,即:
     *      带有mark标识的包被丢弃,同时紧靠其前或其后的相邻的包也有丢包的情况,这种情况下,即使下一个帧是完整的帧,也会因为无法判断关键帧的开始,
     *      而被当作非关键帧处理,因此关键帧的统计被延迟.
     */

    if (p_st_sess->ui_a_ip == p_st_packet->src_ip &&
        p_st_sess->ui_b_ip == p_st_packet->dst_ip &&
        p_st_sess->us_a_port == p_st_packet->src_port &&
        p_st_sess->us_b_port == p_st_packet->dst_port)
    {
        // 设置第一帧的第一个包的时间
        if (p_st_sess->ul_a2b_fbt == 0L)
            p_st_sess->ul_a2b_fbt = rtp_get_timestamp(&(p_st_packet->st_timestamp));

        if (p_st_sess->i_a2b_is_ff == APM_RTP_FLAG_UNSET)
            p_st_sess->i_a2b_is_ff = FR_YES;

        if (p_st_sess->l_a2b_seq != APM_RTP_FLAG_UNSET)
        {
            // 当前帧不完整
            if (us_seq != p_st_sess->l_a2b_seq + 1)
            {
                // 前后两个包的时间戳不同,说明当前包处于一个新的帧,且中间过程丢失了一个包,说明丢的这个包带有mark标识,则记当前包为一个完整的帧
                if (p_st_sess->ui_a2b_ppt != 0 && p_st_sess->ui_a2b_ppt != ui_timestamp &&
                    us_seq - (p_st_sess->l_a2b_seq + 1) == 1)
                {
                    p_st_sess->i_a2b_is_ff = FR_YES;
                    p_st_sess->ul_a2b_bytes = 0;
                }
                else
                    p_st_sess->i_a2b_is_ff = FR_NO;
                RTP_PRINT_FATAL("l_a2b_seq:[%lu] us_seq:[%u] lost:[%lu]\n", p_st_sess->l_a2b_seq, us_seq, us_seq - p_st_sess->l_a2b_seq);
                p_st_sess->ul_a2b_pluff += (us_seq - (p_st_sess->l_a2b_seq + 1));
            }
        }

        p_st_sess->l_a2b_seq = us_seq;
        RTP_PRINT_FATAL("l_a2b_seq:[%lu] i_a2b_is_ff:[%d]\n", p_st_sess->l_a2b_seq, p_st_sess->i_a2b_is_ff);

        // 如果第一个完整帧的时间未设置，则累计每一帧的字节数
        if (p_st_sess->ul_a2b_ffft == 0L)
            p_st_sess->ul_a2b_bytes += ui_frame_len;

        // 如果当前帧不是完整帧,则字节计数清零
        if (p_st_sess->i_a2b_is_ff == FR_NO)
            p_st_sess->ul_a2b_bytes = 0;

        // 记录当前包的RTP协议timestamp
        p_st_sess->ui_a2b_ppt = ui_timestamp;

        // 当前帧结束
        if (c_marker != 0)
        {
            // 如果当前帧是完整的帧，而且第一个完整帧的结束时间未设置
            if (p_st_sess->i_a2b_is_ff == FR_YES && p_st_sess->ul_a2b_ffft == 0L)
            {
                p_st_sess->ul_a2b_ffft = rtp_get_timestamp(&(p_st_packet->st_timestamp));
                RTP_PRINT_ERR("ul_a2b_ffft:%lu ul_a2b_pluff:%lu ul_a2b_bytes:%lu\n", p_st_sess->ul_a2b_ffft, p_st_sess->ul_a2b_pluff, p_st_sess->ul_a2b_bytes);
            }

            p_st_sess->i_a2b_is_ff = FR_YES;
        }
    }
    else if (p_st_sess->ui_a_ip == p_st_packet->dst_ip &&
             p_st_sess->ui_b_ip == p_st_packet->src_ip &&
             p_st_sess->us_a_port == p_st_packet->dst_port &&
             p_st_sess->us_b_port == p_st_packet->src_port)
    {
        // 设置第一帧的第一个包的时间
        if (p_st_sess->ul_b2a_fbt == 0L)
            p_st_sess->ul_b2a_fbt = rtp_get_timestamp(&(p_st_packet->st_timestamp));

        if (p_st_sess->i_b2a_is_ff == APM_RTP_FLAG_UNSET)
            p_st_sess->i_b2a_is_ff = FR_YES;

        if (p_st_sess->l_b2a_seq != APM_RTP_FLAG_UNSET)
        {
            // 当前帧不完整
            if (us_seq != p_st_sess->l_b2a_seq + 1)
            {
                // 前后两个包的时间戳不同,说明当前包处于一个新的帧,且中间过程丢失了一个包,说明丢的这个包带有mark标识,则记当前包为一个完整的帧
                if (p_st_sess->ui_b2a_ppt != 0 && p_st_sess->ui_b2a_ppt != ui_timestamp &&
                    us_seq - (p_st_sess->l_b2a_seq + 1) == 1)
                {
                    p_st_sess->i_b2a_is_ff = FR_YES;
                    p_st_sess->ul_b2a_bytes = 0;
                }
                else
                    p_st_sess->i_b2a_is_ff = FR_NO;
                RTP_PRINT_FATAL("l_b2a_seq:[%lu] us_seq:[%u] lost:[%lu]\n", p_st_sess->l_b2a_seq, us_seq, us_seq - p_st_sess->l_b2a_seq);
                p_st_sess->ul_b2a_pluff += (us_seq - (p_st_sess->l_b2a_seq + 1));
            }
        }

        p_st_sess->l_b2a_seq = us_seq;
        RTP_PRINT_FATAL("l_b2a_seq:[%lu] i_b2a_is_ff:[%d]\n", p_st_sess->l_b2a_seq, p_st_sess->i_b2a_is_ff);

        // 如果第一个完整帧的时间未设置，则累计每一帧的字节数
        if (p_st_sess->ul_b2a_ffft == 0L)
            p_st_sess->ul_b2a_bytes += ui_frame_len;

        // 如果当前帧不是完整帧,则字节计数清零
        if (p_st_sess->i_b2a_is_ff == FR_NO)
            p_st_sess->ul_b2a_bytes = 0;

        // 记录当前包的RTP协议timestamp
        p_st_sess->ui_b2a_ppt = ui_timestamp;

        // 当前帧结束
        if (c_marker != 0)
        {
            // 如果当前帧是完整的帧，而且第一个完整帧的结束时间未设置
            if (p_st_sess->i_b2a_is_ff == FR_YES && p_st_sess->ul_b2a_ffft == 0L)
            {
                p_st_sess->ul_b2a_ffft = rtp_get_timestamp(&(p_st_packet->st_timestamp));
                RTP_PRINT_ERR("ul_b2a_ffft:%lu ul_b2a_pluff:%lu ul_b2a_bytes:%lu\n", p_st_sess->ul_b2a_ffft, p_st_sess->ul_b2a_pluff, p_st_sess->ul_b2a_bytes);
            }

            p_st_sess->i_b2a_is_ff = FR_YES;
        }
    }

    // 如果第一个完整帧结束,即被设置了完整帧结束时间
    if (p_st_sess->ul_a2b_ffft != 0L || p_st_sess->ul_b2a_ffft != 0L)
    {
        if (g_i_connect_in_collect == FR_NO)
        {
            RTP_PRINT_EMERG("first full frame is done, start to find the related sip ct information...\n");
            __rtp_get_related_sip_info_and_report(p_st_conn, p_st_sess, p_st_hash_report, &i_analyze_ret);
        }
        else
        {
            RTP_PRINT_EMERG("first full frame is done, start to report RTP response...\n");
            __report_rtp_response(p_st_conn, p_st_sess, p_st_hash_report, &i_analyze_ret);
        }
    }
    else if (ui_skip_len < ui_payload_len)
    {
        p_payload += ui_skip_len;
        ui_payload_len -= ui_skip_len;
        goto do_frame;
    }

    RTP_PRINT("### i_analyze_ret success:[%d]\n\n", i_analyze_ret);
    return i_analyze_ret;
rtp_analyze_todo:
    RTP_PRINT("### i_analyze_ret todo:[%d]\n\n", APM_L7_ANALYZE_TODO);
    return APM_L7_ANALYZE_TODO;
rtp_analyze_fail:
    RTP_PRINT_EMERG("### i_analyze_ret fail:[%d]\n\n", APM_L7_ANALYZE_FAIL);
    return APM_L7_ANALYZE_FAIL;
}

/*******************************************************************************
*  FUNC     :  释放 L7session_rtp
*  ARGS     :  **pp_st_session              (L7session_rtp)
*  RTN      :  void
*  NOTE     :
*******************************************************************************/
FR_STATIC void __rtp_destroy_session(L7session_rtp **pp_st_session)
{
    if (pp_st_session == NULL || *pp_st_session == NULL)
        return;

    rtp_free((void **) pp_st_session);
    return;
}

/*******************************************************************************
*  FUNC     :  释放RTP private数据
*  ARGS     :  *p_st_session_private_in                 (RTP的私有信息)
*  RTN      :  0                                        (FR_SUCC)
*              1                                        (FR_FAIL)
*  NOTE     :
*******************************************************************************/
int rtp_session_destory(void *p_st_session_private_in)
{
    RTP_PRINT(KBLUE "\nrtp_session_destory run\n" RESET);
    if (p_st_session_private_in == NULL)
        return FR_SUCC;

    L7session_rtp_private *p_st_private = (L7session_rtp_private *) p_st_session_private_in;

    __rtp_destroy_session(&(p_st_private->p_st_sess));

    rtp_free((void **) &p_st_private);

    RTP_PRINT_INFO("leave rtp_session_destory\n");
    return FR_SUCC;
}
